package worker;

import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.PriorityQueue;

import common.JobObject;
import common.MasterObject;
import common.WorkerObject;


public class Worker {

	//static ArrayList subscriptionList = new ArrayList();
	static ArrayList<JobObject> jobs = new ArrayList<JobObject>();
	static ArrayList<JobObject> completedJobList = new ArrayList<JobObject>();
	static InetAddress registryIP;
	static int registryUDPPort;
	static int masterTCPPort = 5002;
	static int workerTCPPort;
	static int workerPrice;
	static WorkerObject worker = new WorkerObject();
	// the queue for jobs
	static PriorityQueue<JobObject> jobQueue = new PriorityQueue<JobObject>(10, new SortByPriority("ACS"));
	static Socket server;
	static MasterObject master = new MasterObject();

    static boolean killJob = false;

	public static void main(String args[]) {
		// arguments checking in initialization
		if (args.length!=4){
			System.out.println("Usage: java Worker <Registry IP> <Registry UDP Port> <Worker Port> <Worker Price>");
			return;
		}

		// read arguments for initializing Worker
		String strRegistryIP = args[0];
		try {
			registryIP = InetAddress.getByName(strRegistryIP);
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
            System.out.println("SYSINFO - Connection Error:Worker cannot connect to Master server, please try later.");
		}
		registryUDPPort = Integer.parseInt(args[1]);
		workerTCPPort = Integer.parseInt(args[2]);
		workerPrice = Integer.parseInt(args[3]);
		
		// worker object
		try {
			worker.setIp(InetAddress.getLocalHost().getHostAddress());
		} catch (UnknownHostException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
            System.out.println("SYSINFO - Connection Error:Worker cannot connect to Master server, please try later.");
		}
		worker.setPort(workerTCPPort);
		worker.setPrice(workerPrice);
		
		System.out.println("DEBUG - Worker IP: "+worker.getIp());
		System.out.println("DEBUG - Worker Port: "+worker.getPort());
		System.out.println("DEBUG - Worker Price: "+worker.getPrice());

		System.out.println("SYSINFO - Worker has been started up successfully.");
		try {
			System.out.println("SYSINFO - Worker subscription service works on address: "+InetAddress.getLocalHost().getHostAddress()+":"+workerTCPPort);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}

		//Initialize a WorkerService instance
		WorkerService workerService = new WorkerService(registryIP, registryUDPPort, masterTCPPort, worker, jobQueue , completedJobList );
		
		RegisterThread registerThread = new RegisterThread(workerService);
		registerThread.start();
		
		MasterRequestThread masterRequestThread = new MasterRequestThread(worker, workerService, jobQueue);
		masterRequestThread.start();
		
		
		ExecuteJobs workerthread = new ExecuteJobs(jobQueue, workerService);
		workerthread.start();
		
		CompletedJobThread competedJobThread = new CompletedJobThread(completedJobList, workerService);
		competedJobThread.start();

        //debug stop the execution thread
        while (true) {
            try {
                Thread.sleep(1000);
                if (killJob) {
                    workerthread.stop();
                    System.out.println("SYSINFO - execution thread is killed...");
                    //restart the execution thread
                    jobQueue.clear();
                    workerthread = new ExecuteJobs(jobQueue, workerService);
                    workerthread.start();
                    killJob = false;
                    System.out.println("SYSINFO - execution thread is restarted again...");
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }

	}

}

// thread for registering on Registry
class RegisterThread extends Thread {

	private WorkerService workerService;

	public RegisterThread(WorkerService workerService) {
		this.workerService = workerService;
	}

	public void run() {
		while (true) {
			workerService.register();
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class ReceiveJobThread extends Thread {

	private PriorityQueue<JobObject> jobQueue;
	private WorkerService workerService;

	public ReceiveJobThread (PriorityQueue<JobObject> jobQueue, WorkerService workerService) {
		this.jobQueue = jobQueue;
		this.workerService = workerService;
	}

	public void run() {
		// TODO Auto-generated method stub
		try {
			workerService.ReceiveJobs(jobQueue, workerService);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

// thread for executing jobs assigned from clients
class ExecuteJobs extends Thread {
	
	private PriorityQueue<JobObject> jobQueue = new PriorityQueue<JobObject>();
	private WorkerService workerService;
	
	public ExecuteJobs(PriorityQueue<JobObject> jobQueue, WorkerService workerService) {
		this.jobQueue = jobQueue;
		this.workerService = workerService;
	}
	
	public void run() {
		System.out.println("SYSINFO - Worker Execution service start");
		while (true) {
         //   System.out.println("DEBUG - Worker Execution thread is running");
			if (!jobQueue.isEmpty()) {
				System.out.println("DEBUG - jobQueue is not empty.");
				JobObject thisJob = jobQueue.poll();
				workerService.executeFile(thisJob);
				System.out.println("SYSINFO: Job("+thisJob.getJobID()+") from client(X) has been completed."); // NTD
				// notify the jobQueue(load) change to Server
				workerService.register();
			}
			try {
				sleep(1000); // Note: the time have to be set (for windows)
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

class CompletedJobThread extends Thread {

	private ArrayList<JobObject> completedJobList;
	private WorkerService workerService;

	public CompletedJobThread(ArrayList<JobObject> completedJobList,
			WorkerService workerService) {
		this.completedJobList = completedJobList;
		this.workerService = workerService;
	}

	public void run() {
		while(true){
			if(completedJobList != null&& completedJobList.size()!= 0 ){
				System.out.println("DEBUG - completedJobList is not empty.");
				JobObject job = completedJobList.remove(0);
				workerService.sendBackResult(job);
			}
			try {
				sleep(1000); // Note: the time have to be set (for windows)
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
